<template>
    <div style="display: flex; height: 820px;">
        <!-- 左侧组件库 -->
        <div style="width: 200px; overflow-y: auto;">
            <h3>组件库</h3>
            <div v-for="(type, index) in componentTypes" :key="index" draggable="true" @dragstart="onDragStart(type)">
                <t-button block variant="outline">{{ type }}</t-button>
            </div>
            <t-button theme="primary" @click="exportPageJSON">导出页面JSON</t-button>
            <!-- 组件库下方新增截图按钮 -->
            <t-button theme="primary" v-if="components.length > 0" @click="captureSmartScreenshot(true)" style="margin-top: 10px;">
                截图页面
            </t-button>
        </div>

        <!-- 中间设计区域 -->
        <div class="design-area" ref="designArea" @dragover.prevent @drop="onDrop">
            <!-- phone-mockup 容器 -->
            <div class="phone-mockup" ref="phoneMockup">
                <vue3-draggable-resizable
                        v-for="(comp, index) in components"
                        :key="comp.id"
                        :x="comp.style.x"
                        :y="comp.style.y"
                        :w="comp.style.width"
                        :h="comp.style.height"
                        :parent="true"
                        @dragging="onDragging($event, comp)"
                        @resizing="onResizing($event, comp)"
                        :active="comp === selectedComponent"
                        @activated="select(comp)"
                        >
                    <!-- 删除按钮 -->
                            <button
                                    class="delete-btn"
                                    @click.stop="deleteComponent(comp.id)"
                                    title="删除组件"
                            >
                                ×
                            </button>
                            <RenderEngine :config="[comp]" />
                </vue3-draggable-resizable>
            </div>
        </div>

        <!-- 右侧属性编辑器 -->
        <div style="width: 300px; padding-left: 10px; border-left: 1px solid #ccc; overflow-y: auto;">
            <PropertyEditor :selected="selectedComponent" />
            <StyleEditor :selected="selectedComponent" />
        </div>
    </div>
</template>

<script setup>
    import { ref, nextTick, onMounted  } from 'vue'
    import axios from 'axios'
    import Vue3DraggableResizable from 'vue3-draggable-resizable'
    import RenderEngine from '../components/RenderEngine.vue'
    import PropertyEditor from './PropertyEditor.vue'
    import StyleEditor from './StyleEditor.vue'
    import { componentMap } from '../components/component-map.js'
    import html2canvas from 'html2canvas'

    const components = ref([])
    const selectedComponent = ref(null)
    const componentTypes = Object.keys(componentMap)
    const draggingType = ref(null)

    const designArea = ref(null)
    const phoneMockup = ref(null)

    import { useRoute, useRouter  } from 'vue-router'
    const route = useRoute()
    const router = useRouter()
    // 页面加载时检查是否有传入数据
    onMounted(() => {
        const jsonStr = route.query.data
        if (jsonStr) {
            try {
                const jsonObj = JSON.parse(decodeURIComponent(jsonStr))
                if (jsonObj.page && Array.isArray(jsonObj.page.components)) {
                    components.value = jsonObj.page.components.map(c => ({
                        ...c,
                        id: c.id || Date.now().toString()
                    }))
                }
            } catch (e) {
                console.error('反显JSON解析失败', e)
            }
        }
    })

    const onDragStart = (type) => {
        draggingType.value = type
    }

    const onDrop = (e) => {
        if (!phoneMockup.value) return

        // 获取 phoneMockup 边界及设计区位置
        const phoneRect = phoneMockup.value.getBoundingClientRect()
        const designRect = designArea.value.getBoundingClientRect()

        // 计算组件落点坐标相对 phoneMockup 左上角
        let x = e.clientX - phoneRect.left
        let y = e.clientY - phoneRect.top

        // 限制组件必须放在 phoneMockup 内
        x = Math.min(Math.max(0, x), phoneRect.width)
        y = Math.min(Math.max(0, y), phoneRect.height)

        const defaultStyle = componentMap[draggingType.value]?.defaultStyle || {}
        const defaultProps = componentMap[draggingType.value]?.defaultProps || {}

        components.value.push({
            id: Date.now().toString(),
            type: draggingType.value,
            props: { ...defaultProps },
            style: { ...defaultStyle, x, y }
        })

        draggingType.value = null
    }

    const select = (comp) => {
        selectedComponent.value = comp
    }

    const onDragging = ({ x, y }, comp) => {
        if (!phoneMockup.value) return

        const phoneRect = phoneMockup.value.getBoundingClientRect()

        // 组件尺寸
        const compWidth = comp.style.width || 0
        const compHeight = comp.style.height || 0

        // 限制左上边界不能小于0，右下边界不能超过phoneMockup大小
        let limitedX = Math.min(Math.max(0, x), phoneRect.width - compWidth)
        let limitedY = Math.min(Math.max(0, y), phoneRect.height - compHeight)

        comp.style.x = limitedX
        comp.style.y = limitedY
    }


    const onResizing = (resizeInfo, comp) => {
        const { w, h } = resizeInfo
        if (!comp.originalWidth) comp.originalWidth = comp.style.width
        if (!comp.originalHeight) comp.originalHeight = comp.style.height

        // 限制最大尺寸不能超过 phoneMockup 宽高
        if (!phoneMockup.value) return

        const phoneRect = phoneMockup.value.getBoundingClientRect()

        comp.style.width = Math.min(w, phoneRect.width - comp.style.x)
        comp.style.height = Math.min(h, phoneRect.height - comp.style.y)
    }

    // 新增删除方法
    const deleteComponent = (id) => {
        const index = components.value.findIndex(c => c.id === id)
        if (index !== -1) {
            // 如果删除的是当前选中组件，清空选中
            if (selectedComponent.value?.id === id) {
                selectedComponent.value = null
            }
            components.value.splice(index, 1)
        }
    }


    /** 上传截图到服务器，返回图片地址 */
    const uploadImage = async (base64Image) => {
        try {
            // base64 转 Blob
            const blob = await fetch(base64Image).then(res => res.blob())
            const formData = new FormData()
            formData.append('files', new File([blob], `screenshot_${Date.now()}.png`, { type: 'image/png' }))

            const res = await axios.post('http://localhost:8080/upload/image', formData, {
                headers: { 'Content-Type': 'multipart/form-data' }
            })

            if (Array.isArray(res.data) && res.data.length > 0) {
                return res.data[0]  // 返回第一个图片地址
            } else {
                console.warn('上传成功但未返回有效路径', res.data)
                return ''
            }
        } catch (e) {
            console.error('图片上传失败', e)
            return ''
        }
    }


    /** 智能截图，返回 base64 图片 */
    const captureSmartScreenshot = async (screenshot) => {
        if (!phoneMockup.value || components.value.length === 0) return null

        const phoneEl = phoneMockup.value
        const scale = 2

        const cardComponents = components.value.filter(c => c.type === 'Card')
        const activeRange = cardComponents.length > 0 ? cardComponents : components.value

        const yList = activeRange.map(c => ({ top: c.style.y, bottom: c.style.y + c.style.height }))
        const minY = Math.min(...yList.map(i => i.top))
        const maxY = Math.max(...yList.map(i => i.bottom))
        const cropHeight = (maxY - minY) * scale

        // 临时隐藏不需要的元素
        const deleteBtns = phoneEl.querySelectorAll('.delete-btn')
        deleteBtns.forEach(btn => (btn.style.display = 'none'))

        const inputReplacements = []
        phoneEl.querySelectorAll('.el-input').forEach(el => {
            const input = el.querySelector('input')
            if (input) {
                const span = document.createElement('span')
                span.textContent = input.value
                el.style.display = 'none'
                el.parentElement.insertBefore(span, el)
                inputReplacements.push({ el, span })
            }
        })

        const removedCardStyle = []
        phoneEl.querySelectorAll('.el-card').forEach(card => {
            removedCardStyle.push({ el: card, boxShadow: card.style.boxShadow, borderRadius: card.style.borderRadius })
            card.style.boxShadow = 'none'
            card.style.borderRadius = '0'
        })

        await nextTick()

        try {
            const fullCanvas = await html2canvas(phoneEl, { useCORS: true, allowTaint: false, backgroundColor: null, scale })
            const cropped = document.createElement('canvas')
            cropped.width = fullCanvas.width
            cropped.height = cropHeight
            cropped.getContext('2d').drawImage(fullCanvas, 0, minY * scale, fullCanvas.width, cropHeight, 0, 0, fullCanvas.width, cropHeight)

            const imgUrl = cropped.toDataURL('image/png')

            if (screenshot) {
                const link = document.createElement('a')
                link.href = imgUrl
                link.download = `screenshot-${Date.now()}.png`
                link.click()
            }

            return imgUrl
        } catch (e) {
            console.error('截图失败', e)
            return null
        } finally {
            deleteBtns.forEach(btn => (btn.style.display = ''))
            inputReplacements.forEach(({ el, span }) => { span.remove(); el.style.display = '' })
            removedCardStyle.forEach(({ el, boxShadow, borderRadius }) => {
                el.style.boxShadow = boxShadow
                el.style.borderRadius = borderRadius
            })
        }
    }


    /** 导出页面并保存模板 */
    const exportPageJSON = async () => {
        const pageJSON = {
            page: {
                id: "page-1",
                type: "Page",
                style: {
                    width: phoneMockup.value.clientWidth,
                    height: phoneMockup.value.clientHeight,
                    backgroundColor: "#fff",
                    position: "relative"
                },
                components: components.value.map(c => ({
                    id: c.id,
                    type: c.type,
                    props: c.props,
                    style: c.style
                }))
            }
        }

        const jsonStr = JSON.stringify(pageJSON, null, 2)

        const screenshotBase64 = await captureSmartScreenshot(false)
        if (!screenshotBase64) {
            console.warn('截图失败，终止保存')
            return
        }

        const imageUrl = await uploadImage(screenshotBase64)
        if (!imageUrl) {
            console.warn('图片上传失败，终止保存')
            return
        }

        const tempId = route.query.tempId
        console.log(tempId)
        const template = { json: jsonStr, type: 'mini', cover: imageUrl }
        if (tempId) {
            template.id = tempId
        }

        try {
            await axios.post('http://localhost:8080/data/template', template)
             router.push({
                path: '/templates'
            })
        } catch (e) {
            console.error('保存模板失败', e)
        }
    }
</script>

<style>
    .design-area {
        flex: 1;
        margin: 10px;
        height: 900px;
        background: #f4f4f4;
        border: 1px solid #ccc;
        position: relative;
        display: flex;
        justify-content: center; /* 水平居中 phone-mockup */
        align-items: center; /* 垂直居中 phone-mockup */
    }

    .phone-mockup {
        width: 430px;
        height: 844px;
        background: #fff;
        border: 1px solid #ccc;
        box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
        position: relative;  /* 使里面拖拽元素的定位相对于此 */
        overflow: hidden;
    }

    .delete-btn {
        position: absolute;
        top: 2px;
        right: 2px;
        width: 20px;
        height: 20px;
        border: none;
        background: rgba(255, 0, 0, 0.8);
        color: white;
        font-weight: bold;
        border-radius: 50%;
        cursor: pointer;
        z-index: 10;
        line-height: 18px;
        padding: 0;
        user-select: none;
    }
    .delete-btn:hover {
        background: rgba(255, 0, 0, 1);
    }

</style>
